package de.stefanocke.roo.wicket.templates

import de.stefanocke.roo.wicket.Stereotypes

class DecoratorFragmentTemplate extends JavaHtmlTemplate{
	
	override String javaClassNameWhenNew(){
		"DecoratorFragment"
	}
	
	override String stereotype(){
		Stereotypes::DECORATOR_FRAGMENT
	}
	
	override subPackageToUseWhenNew(){
		"common"
	}
		
	override buildJavaContents(){
		import_("org.apache.wicket.Component")
		import_("org.apache.wicket.MarkupContainer")
		import_("org.apache.wicket.markup.html.basic.Label")
		import_("org.apache.wicket.markup.html.form.FormComponent")
		import_("org.apache.wicket.markup.html.form.LabeledWebMarkupContainer")
		import_("org.apache.wicket.markup.html.form.SimpleFormComponentLabel")
		import_("org.apache.wicket.markup.html.panel.Fragment")
		import_("org.apache.wicket.model.IModel")
		import_("org.apache.wicket.model.ResourceModel")
		
		val ILabeled = import_(typeof(ILabeledTemplate).generatedJavaType)
		
		'''
		/**
		 * A fragment used to decorate a component, for example a form field. Propagates
		 * its model to the decorated component. This allows the decorator to be used
		 * with CPM as if they were the component itself.
		 * <p>
		 * Note that it's not a decorator in GOF sense, since it does not implement the
		 * interface of the decorated component.
		 * <p>
		 * Fragments are used here, since decorations are supposed to have little
		 * markup.
		 * <p>
		 * Cases, where the decorated component has little markup itself (f.e. <input>
		 * or <select> tag for a form field) are also supported, by optionally creating
		 * an inner fragment.
		 * <p>
		 * By default, a label is created as part of the decorator. If the component is
		 * a {@link LabeledWebMarkupContainer}, a FormComponentLabel will be created and
		 * the according markup must use the <label> tag.
		 * 
		 * @author stoc
		 * 
		 * @param <C> the type of the component
		 */
		«stereotypeAnnotation»
		public abstract class DecoratorFragment<C extends Component> extends Fragment implements «ILabeled» {
		
			/***/
			private static final long serialVersionUID = 1L;
		
			/***/
			protected static final String COMPONENT_ID = "comp";
		
			/***/
			protected static final String INNER_FRAGMENT_ID = "container";
		
			/***/
			protected static final String LABEL_ID = "label";
		
			public DecoratorFragment(String id, String markupId, MarkupContainer markupProvider) {
				super(id, markupId, markupProvider);
				init(markupProvider, null);
		
			}
		
			public DecoratorFragment(String id, String markupId, String innerFragmentMarkupId, MarkupContainer markupProvider) {
				super(id, markupId, markupProvider);
				init(markupProvider, innerFragmentMarkupId);
		
			}
		
			private void init(MarkupContainer markupProvider, String innerFragmentMarkupId) {
				C component = createComponent(COMPONENT_ID, markupProvider);
				initDecoration(component);
				// create and add the inner fragment, if requested
				if (innerFragmentMarkupId != null) {
					Fragment innerFragment = new Fragment(INNER_FRAGMENT_ID, innerFragmentMarkupId, markupProvider);
					getInnerFragmentContainer().add(innerFragment);
				}
				// create and add the label
				Component label = createLabel(LABEL_ID, component);
				getLabelContainer().add(label);
				// add the component
				getComponentContainer().add(component);
			}
		
			protected Component createLabel(String labelId, C component) {
		
				// TODO: LabeledWebMarkupContainer currently does not provide setLabel
				// method. Thus, FormComponentLabel support (<label for="...">) is only 
				// provided for FormComponents.
				if (component instanceof FormComponent) {
					FormComponent<?> lwmc = (FormComponent<?>) component;
					lwmc.setLabel(new ResourceModel(getId()));
					return new SimpleFormComponentLabel(LABEL_ID, lwmc);
				} else {
					return new Label(LABEL_ID, new ResourceModel(getId()));
				}
			}
		
			@Override
			public void setLabel(IModel<String> labelModel) {
				C component = getComponent();
				if (component instanceof FormComponent) {
					FormComponent<?> lwmc = (FormComponent<?>) component;
					lwmc.setLabel(labelModel);
				}
				getLabelComponent().setDefaultModel(labelModel);
			}
		
			@SuppressWarnings("unchecked")
			@Override
			public IModel<String> getLabel() {
				return (IModel<String>) getLabelComponent().getDefaultModel();
			}
		
			/**
			 * Inits the decorator. Must not add the component to the hierarchy.
			 * 
			 * @param component
			 */
			protected void initDecoration(C component) {
			}
		
			/**
			 * Gets the container where to add the component. By default, this is the
			 * inner fragment, if existing, and otherwise it is this object.
			 * 
			 * @return the container for the component.
			 */
			protected MarkupContainer getComponentContainer() {
				MarkupContainer innerFragment = getInnerFragment();
				if (innerFragment != null) {
					return innerFragment;
				} else {
					return this;
				}	
			}
		
			/**
			 * Gets the decorated component by searching for a component with ID
			 * {@link #COMPONENT_ID} in the container as determined by
			 * {@link #getComponentContainer()}.
			 * 
			 * @return the component
			 */
			@SuppressWarnings("unchecked")
			public C getComponent() {
				return (C) getComponentContainer().get(COMPONENT_ID);
			}
		
			/**
			 * 
			 * @return the inner fragment or null, if no inner fragment has been
			 *         created.
			 */
			private MarkupContainer getInnerFragment() {
				return (MarkupContainer) getInnerFragmentContainer().get(INNER_FRAGMENT_ID);
			}
		
			/**
			 * 
			 * @return the container for the inner fragment. By default, this object.
			 */
			protected MarkupContainer getInnerFragmentContainer() {
				return this;
			}
		
			public Component getLabelComponent() {
				return getLabelContainer().get(LABEL_ID);
			}
		
			/**
			 * 
			 * @return the container for the label. By default, this object.
			 */
			protected MarkupContainer getLabelContainer() {
				return this;
			}
		
			/**
			 * Creates the component to be decorated.
			 * 
			 * @param id
			 *            the id to be used for the component
			 * @param markupProvider
			 *            the markupProvider
			 * @return the component.
			 */
			protected C createComponent(String id, MarkupContainer markupProvider) {
				return createComponent(id);
			}
		
			protected C createComponent(String id) {
				throw new UnsupportedOperationException("Please override one of the createComponent methods!");
			}
		
			@Override
			protected void onInitialize() {
				super.onInitialize();
				// This is the first time, the CompoundPropertyModel of the parent
				// is available.
				// So, onModelChanged is called, to set the proper model for the
				// form component.
				onModelChanged();
			}
		
			/**
			 * Propagate the model change to the decorated component.
			 */
			@Override
			protected void onModelChanged() {
				super.onModelChanged();
				getComponent().setDefaultModel(getDefaultModel());
			}
		}
		'''
	}
	
}